﻿using Microsoft.Build.Evaluation;
using NuGet;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OD.ProjectCustomizationTool.Helpers
{
    // This code has been generated from the 'classSingleton' custom snippet.

    /// <summary>
    /// NuGetHelper class implements the thread-safe version of the Singleton 
    /// design pattern. This class is designed to be used to provide the singleton 
    /// across threads in a multi-threaded application.
    /// <see cref="http://msdn.microsoft.com/en-us/library/ms998558.aspx"/>
    /// </summary>
    public sealed class NuGetHelper
    {
        #region Singleton properties

        private static Object synchronizedRoot = new Object();

        private static volatile NuGetHelper current;

        /// <summary>
        /// Public property that can only get the single NuGetHelper instance.
        /// </summary>
        public static NuGetHelper Current
        {
            get
            {
                if (NuGetHelper.current == null)
                {
                    lock (NuGetHelper.synchronizedRoot)
                    {
                        if (NuGetHelper.current == null)
                        {
                            // Use this lock to ensure that only one thread is access
                            // this block of code at once.
                            NuGetHelper.current = new NuGetHelper();
                        }
                    }
                }
                return NuGetHelper.current;
            }
        }

        #endregion

        #region Properties

        private const String PackageSource = "https://packages.nuget.org/api/v2";
        private readonly IPackageRepository repository;

        #endregion

        #region Constructors

        /// <summary>
        /// Make the default constructor private, so that no one can directly create a NuGetHelper instance.
        /// </summary>
        private NuGetHelper()
        {
            //Connect to the official package repository
            this.repository = PackageRepositoryFactory.Default.CreateRepository(NuGetHelper.PackageSource);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Retieve the latest version of a package.
        /// <see cref="NuGet.PackageRepositoryExtensions.FindPackagesById"/>
        /// </summary>
        /// <param name="packageId">The package identifier.</param>
        /// <returns>Returns the found package.</returns>
        public IPackage GetLatestVersion(String packageId)
        {
            if (String.IsNullOrEmpty(packageId))
            {   // This code has been generated from the 'argnullString' custom snippet.
                throw new ArgumentNullException("packageId");
            }
            if (String.IsNullOrEmpty(packageId.Trim()))
            {   // This code has been generated from the 'argnullString' custom snippet.
                throw new ArgumentException("The argument 'packageId' contains only white spaces.", "packageId");
            }

            var packages = this.repository
                .FindPackagesById(packageId);

            return packages
                .Where(x => x.IsLatestVersion)
                .FirstOrDefault();
        }

        /// <summary>
        /// Install the latest version of the desired package.
        /// <see cref="NuGet.PackageManager.InstallPackage(string, SemanticVersion)"/>
        /// </summary>
        /// <param name="packageId">The package identifier.</param>
        /// <param name="project">The project to which the install has to be done.</param>
        /// <param name="logger">The log mechanism.</param>
        /// <returns>Returns the found and installed package.</returns>
        public IPackage InstallPackage(String packageId, Project project, ILogger logger)
        {
            if (project == null)
            {   // This code has been generated from the 'argnull' custom snippet.
                throw new ArgumentNullException("project");
            }

            return this.InstallPackage(packageId, project.GetSolutionFullPath(), logger);
        }

        /// <summary>
        /// Install the latest version of the desired package.
        /// <see cref="NuGet.PackageManager.InstallPackage(string, SemanticVersion)"/>
        /// </summary>
        /// <param name="packageId">The package identifier.</param>
        /// <param name="solutionFullPath">The solution full path to which the install has to be done.</param>
        /// <param name="logger">The log mechanism.</param>
        /// <returns>Returns the found and installed package.</returns>
        public IPackage InstallPackage(String packageId, String solutionFullPath, ILogger logger)
        {
            if (String.IsNullOrEmpty(packageId))
            {   // This code has been generated from the 'argnullString' custom snippet.
                throw new ArgumentNullException("packageId");
            }
            if (String.IsNullOrEmpty(packageId.Trim()))
            {   // This code has been generated from the 'argnullString' custom snippet.
                throw new ArgumentException("The argument 'packageId' contains only white spaces.", "packageId");
            }
            if (String.IsNullOrEmpty(solutionFullPath))
            {   // This code has been generated from the 'argnullString' custom snippet.
                throw new ArgumentNullException("solutionFullPath");
            }
            if (String.IsNullOrEmpty(solutionFullPath.Trim()))
            {   // This code has been generated from the 'argnullString' custom snippet.
                throw new ArgumentException("The argument 'solutionFullPath' contains only white spaces.", "solutionFullPath");
            }
            if (logger == null)
            {   // This code has been generated from the 'argnull' custom snippet.
                throw new ArgumentNullException("logger");
            }

            var packageToInstall = this.GetLatestVersion(packageId);
            if (packageToInstall == null)
            {
                throw new ArgumentException("The package '" + packageId + "' does not exist.", "packageId");
            }

            var packageManager = new PackageManager(
                this.repository,
                Path.Combine(solutionFullPath, Properties.Resources.NuGetPackageFolderName));

            logger.WriteLine("Installing NuGet package '" + packageId + "' in '" + packageManager.LocalRepository.Source + "'.");
            packageManager.InstallPackage(packageId, packageToInstall.Version);
            return packageToInstall;
        }

        #endregion

    }
}
